package usuarios;

import java.io.Serializable;
import java.util.List;
import exceptions.CPFInvalidoException;
import exceptions.EmailInvalidoException;
import exceptions.ListaEnderecoInvalidaException;
import exceptions.NomeInvalidoException;
import exceptions.SenhaInvalidaException;
import exceptions.TelefoneInvalidoException;
import exceptions.UsernameInvalidoException;

public class Pessoa implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private static final String REGEX_NOME = ".*";
	private static final String REGEX_CPF = "\\d{3}.\\d{3}.\\d{3}-\\d{2}|\\d{11}";
	private static final String REGEX_TELEFONE = "\\d{4}-\\d{4}|\\d{8}";
	private static final String REGEX_EMAIL = "\\w+@+\\w+\\.\\w{2,3}";
	private String nome;
	private String cpf;
	private String telefone;
	private String email;
	private List<Endereco> enderecos;
	private String username;
	private String senha;

	/**
	 * Construtor de uma Pessoa.
	 * 
	 * @param nome
	 *            O nome da Pessoa. Nao pode ser null e deve ser uma String com
	 *            no minimo um espaco entre duas cadeias de letras.
	 * @param cpf
	 *            O CPF da Pessoa. Nao pode ser nulo e deve conter 11 digitos ou
	 *            11 digitos separados, inicialmente, 3 a 3 por pontos e os
	 *            ultimos dois digitos antecedidos por um hifen.
	 * @param telefone
	 *            O telefone da Pessoa. O telefone nao pode ser nulo e deve ser
	 *            formado por 8 digitos ou 8 digitos separados 4 a 4 por um
	 *            hifen.
	 * @param email
	 *            O email da Pessoa. O email nao pode ser nulo e deve ter,
	 *            inicialmente, um arroba entre cadeias de caracteres seguidos
	 *            de um ponto e um final de dois ou tres caracteres.
	 * @param enderecos
	 *            A lista de enderecos da Pessoa, o primeiro endereco deve ser o
	 *            principal. Apenas o endereco principal eh obrigatorio para toda
	 *            pessoa.
	 * @param username
	 *            O username da Pessoa. Um username valido possui no minimo 6
	 *            caracteres.
	 * @param senha
	 *            A senha da Pessoa. Uma senha valida possui no minimo 6
	 *            caracteres e nao pode ser igual ao username.
	 * @throws NomeInvalidoException
	 *             Se o nome for null ou nao for uma String com no minimo um
	 *             espaco entre duas cadeias de letras.
	 * @throws CPFInvalidoException
	 *             Se o CPF for nulo ou nao conter 11 digitos ou 11 digitos
	 *             separados, inicialmente, 3 a 3 por pontos e os dois ultimos
	 *             digitos antecedidos por um hifen.
	 * @throws TelefoneInvalidoException
	 *             Se o telefone for nulo ou nao conter 8 digitos ou 8 digitos 4
	 *             a 4 separados por um hifen.
	 * @throws EmailInvalidoException
	 *             Se o email for nulo ou nao ter, inicialmente, um arroba entre
	 *             cadeias de caracteres seguidos de um ponto e um final de dois
	 *             ou tres caracteres.
	 * @throws ListaEnderecoInvalidaException
	 *             Se a lista de enderecos for nula ou nao conter um endereco
	 *             principal na primeira posicao.
	 * @throws UsernameInvalidoException
	 *             Se o username for nulo ou nao conter o minimo de 6
	 *             caracteres.
	 * @throws SenhaInvalidaException
	 *             Se a senha for nula ou igual ao username ou nao conter o
	 *             minimo de 6 caracteres.
	 */

	public Pessoa(String nome, String cpf, String telefone, String email,
			List<Endereco> enderecos, String username, String senha)
			throws NomeInvalidoException, CPFInvalidoException,
			TelefoneInvalidoException, EmailInvalidoException,
			SenhaInvalidaException, UsernameInvalidoException,
			ListaEnderecoInvalidaException {
		if (nome == null || nome.trim().split(" ").length < 2
				|| nome.matches(REGEX_NOME))
			throw new NomeInvalidoException();
		if (cpf == null || !cpf.matches(REGEX_CPF))
			throw new CPFInvalidoException();
		if (telefone == null || !telefone.matches(REGEX_TELEFONE))
			throw new TelefoneInvalidoException();
		if (email == null || !email.matches(REGEX_EMAIL))
			throw new EmailInvalidoException();
		if (enderecos == null || enderecos.size() == 0
				|| !enderecos.get(0).isPrincipal())
			throw new ListaEnderecoInvalidaException();
		if (username == null || username.length() < 6)
			throw new UsernameInvalidoException();
		if (senha == null || senha.length() < 6 || senha.equals(username))
			throw new SenhaInvalidaException();
		this.nome = nome;
		this.cpf = cpf;
		this.telefone = telefone;
		this.email = email;
		this.enderecos = enderecos;
		this.username = username;
		this.senha = senha;
	}

	/**
	 * Recupera o nome da pessoa.
	 * 
	 * @return O nome da pessoa.
	 */
	
	public String getNome() {
		return nome;
	}

	/**
	 * Edita o nome da Pessoa.
	 * 
	 * @param nome
	 *            O novo nome para a Pessoa.
	 */
	
	public void setNome(String nome) {
		this.nome = nome;
	}

	/**
	 * Recupera o CPF da Pessoa.
	 * 
	 * @return O CPF da Pessoa.
	 */
	
	public String getCpf() {
		return cpf;
	}

	/**
	 * Edita o CPF da Pessoa.
	 * 
	 * @param cpf
	 *            O novo CPF da Pessoa.
	 */
	
	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	/**
	 * Recupera o telefone da Pessoa.
	 * 
	 * @return O telefone da Pessoa.
	 */
	
	public String getTelefone() {
		return telefone;
	}

	/**
	 * Edita o telefone da Pessoa.
	 * 
	 * @param telefone
	 *            O novo telefone para a Pessoa.
	 */
	
	public void setTelefone(String telefone) {
		this.telefone = telefone;
	}

	/**
	 * Recupera o Email da Pessoa.
	 * 
	 * @return O Email da Pessoa.
	 */
	
	public String getEmail() {
		return email;
	}

	/**
	 * Edita o Email da Pessoa.
	 * 
	 * @param email
	 *            O novo Email para a Pessoa.
	 */
	
	public void setEmail(String email) {
		this.email = email;
	}

	/**
	 * Recupera a lista de enderecos da Pessoa.
	 * 
	 * @return A lista de enderecos da Pessoa.
	 */
	
	public List<Endereco> getEnderecos() {
		return enderecos;
	}

	/**
	 * Recupera o username da Pessoa.
	 * 
	 * @return O username da Pessoa.
	 */
	
	public String getUsername() {
		return username;
	}

	/**
	 * Edita o username da Pessoa.
	 * 
	 * @param username
	 *            O novo username para a Pessoa.
	 */
	
	public void setUsername(String username) {
		this.username = username;
	}

	/**
	 * Recupera a senha da Pessoa.
	 * 
	 * @return A senha da Pessoa.
	 */
	
	public String getSenha() {
		return senha;
	}

	/**
	 * Edita a senha da Pessoa.
	 * 
	 * @param senha
	 *            A nova senha para a Pessoa.
	 */
	
	public void setSenha(String senha) {
		this.senha = senha;
	}

	/**
	 * Adiciona um novo enderenco na lista de enderecos da Pessoa.
	 * 
	 * @param endereco
	 *            O endereco a ser adicionado na lista de enderecos da Pessoa.
	 * @return true, se o endereco for adicionado, ou false, caso contrario.
	 */
	
	public boolean adicionaEndereco(Endereco endereco) {
		if (endereco != null) {
			this.enderecos.set(0, endereco);
			return true;
		}
		return false;
	}

	/**
	 * Remove um endereco localizado atraves do seu CEP.
	 * 
	 * @param cep
	 *            O CEP do endereco a ser removido.
	 * @return true, se o endereco for removido, e false, caso contrario.
	 */
	
	public boolean removeEndereco(String cep) {
		for (Endereco endereco : enderecos) {
			if (endereco.getCep().equals(cep)) {
				enderecos.remove(endereco);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Calcula o valor HashCode do objeto.
	 * 
	 * @return O valor HashCode do objeto.
	 */
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((cpf == null) ? 0 : cpf.hashCode());
		result = prime * result + ((email == null) ? 0 : email.hashCode());
		result = prime * result
				+ ((enderecos == null) ? 0 : enderecos.hashCode());
		result = prime * result + ((nome == null) ? 0 : nome.hashCode());
		result = prime * result + ((senha == null) ? 0 : senha.hashCode());
		result = prime * result
				+ ((telefone == null) ? 0 : telefone.hashCode());
		result = prime * result
				+ ((username == null) ? 0 : username.hashCode());
		return result;
	}

	/**
	 * Compara se o objeto recebido como parametro eh igual a este.
	 * 
	 * @return true, se o objeto recebido for igual a este, ou false, caso
	 *         contrario.
	 */
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Pessoa)){
			return false;
		}
		Pessoa p = (Pessoa) obj;
		return (p.getCpf().equals(getCpf()));
	}

	/**
	 * Representa a pessoa em String.
	 * 
	 * @return A representacao em String da pessoa.
	 */
	
	@Override
	public String toString() {
		return "Nome: " + nome + "\nCPF: " + cpf + "\nTelefone: " + telefone
				+ "\nEmail: " + email + "\nEnderecos: " + enderecos
				+ "\nUsername=" + username + "\nSenha=" + senha;
	}
}